package cn.ljy.authority.web.controller.backstage;

import cn.ljy.authority.service.BaseService;
import cn.ljy.common.annotation.LimitRequest;
import cn.ljy.common.model.result.ResponseData;
import cn.ljy.common.model.validate.AddWorkValidation;
import cn.ljy.common.model.validate.UpdateWorkValidation;
import cn.ljy.common.utils.page.Pagination;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.constraints.NotEmpty;
import java.io.Serializable;


/**
 * @author ljy
 * 基础控制器，用于规范继承类增删改查方法
 * 说明：
 * 1.如果子类在继承时没有重写本类的方法，则本类的方法和方法内的验证都会生效
 * 2.如果子类的方法在继承时重写了本方法，则只需要super调用相应方法就会执行通用函数即可，但是本类的验证会失效，必须在子类重新验证
 * 例如子类重写的删除方法：
 *     @ApiOperation(value = "批量删除指定的多个角色")
 *     @ApiImplicitParam(name = "ids", value = "要删除的id集合", required = true,paramType = "query",example ="[11,12,13]")
 *     @PreAuthorize("hasAuthority('sys:role:del')")
 *     @Override
 *     public ResponseData deletes(Integer[] ids){
 *         return super.deletes(ids);
 *     }
 *
 * @param <T> 操作对象类
 * @param <Q> 操作对象的查询类
 * @param <K> 操作对象类的主键类型
 */
@RestController
@Slf4j
@Validated //加了后@NoNull之类的注解才能生效，如果只是@valid注解则不需要加上
public abstract class BaseBackstageController<T,Q,K> extends BaseController{

    // 子类必须覆盖此字段，格式如："mes:material",用于@PreAuthorize的权限限定

    @Autowired
    private BaseService<T,Q> baseService;
//    private String authority;
//


    public BaseBackstageController(){

    }

    // 权限检查工具方法（final防止子类覆盖）
    public final String getRequiredAuthority(String action) {
        String prefix = getAuthorityPrefix();
        return prefix != null ? prefix + ":" + action : null;
    }

    // 抽象方法要求子类必须提供权限前缀
    protected abstract String getAuthorityPrefix();

//    protected BaseBackstageController(Class<? extends BaseBackstageController> clazz){
//        System.out.println("父类构造");
//        // 获取SomeClass的Class对象
////        Class<?> clazz = BaseBackstageController.class;
//        // 获取类级别的@PreAuthorize注解
//        PreAuthorize preAuthorizeAnnotation = clazz.getAnnotation(PreAuthorize.class);
//        // 检查注解是否存在
//        if (preAuthorizeAnnotation != null) {
//            // 获取注解的PreAuthorize中表达式 @PreAuthorize("hasAuthority('sys:role')")中的hasAuthority('sys:role')
//            String authorityExpression = preAuthorizeAnnotation.value();
//            //筛选表达式hasAuthority('sys:role')中的sys:role
//            authority=authorityExpression.substring("hasAuthority('".length(), authorityExpression.length() - 2);
//            System.out.println("权限参数：" + authority);
//        }
//    }

    @ApiOperation(value = "查询所有记录列表")
    @LimitRequest
    // 由于本操作不是所有模块都有，一般分页和查询所有记录都是取其一。
    // 所以取消@getmappding，避免因为没写代码被访问到。
    // 子类需要手动添加@GetMapping("/all")
    @GetMapping("/all")
    protected ResponseData query(Q query){
        return ResponseData.success(baseService.findAll(query));
    }

    @ApiOperation(value = "查询符合条件的记录数")
    @LimitRequest
    // 由于本操作不是所有模块都有，一般分页和查询所有记录都是取其一。
    // 所以取消@getmappding，避免因为没写代码被访问到。
    // 子类需要手动添加@GetMapping("/all")
    @GetMapping("/count")
    protected ResponseData count(Q query){
        return ResponseData.success(baseService.count(query));
    }

//    @PreAuthorize("hasAuthority(#this.getAuthorityPrefix() + ':query')")

    @PreAuthorize("@authCheckService.check(this, 'query')")
    @ApiOperation(value = "分页查询记录列表")
    @LimitRequest
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", value = "页码", defaultValue = "1"),
            @ApiImplicitParam(name = "pageSize", value = "每页最大记录数", defaultValue = "20")
    })
    // 由于本操作不是所有模块都有，一般分页和查询所有记录都是取其一。
    // 所以取消@getmappding，避免因为没写代码被访问到。
    // 子类需要手动添加@GetMapping
    @GetMapping
    protected ResponseData query(Integer page, Integer pageSize, Q query){
        Pagination pagination = baseService.findAll(page,pageSize,query);
        return ResponseData.success(pagination);
    }

    @PreAuthorize("@authCheckService.check(this, 'query')")
    @ApiOperation(value = "读取记录")
    @LimitRequest
    @GetMapping("/{id}")
    protected ResponseData get(@PathVariable("id") K id){
        T t=baseService.get((Serializable)id);
        if(t!=null){
            return ResponseData.success(t);
        }else{
            return ResponseData.error("找不到指定记录");
        }
    }

    @PreAuthorize("@authCheckService.check(this, 'edit')")
    @ApiOperation(value = "编辑记录")
    @LimitRequest
    @PutMapping
    protected ResponseData edit(@Validated({UpdateWorkValidation.class}) @RequestBody T t){
        baseService.update(t);
        return ResponseData.success(null);
    }

    @PreAuthorize("@authCheckService.check(this, 'add')")
    @ApiOperation(value = "添加记录")
    @LimitRequest
    @PostMapping
    protected ResponseData add(@Validated({AddWorkValidation.class}) @RequestBody T t){
        baseService.save(t);
        return ResponseData.success(null);
    }

    // 关于@NotEmpty验证。如果继承的子类重写了本类的函数，则验证效果只在子类生效，也需要添加父类相同的验证效果
    // 如果子类没有写@NotEmpty的验证，则无论本类还是子类都不会有验证效果
    @PreAuthorize("@authCheckService.check(this, 'del')")
    @ApiOperation(value = "批量删除记录")
    @LimitRequest
    @ApiImplicitParam(name = "ids", value = "要删除的id集合", required = true,paramType = "query",example ="[11,12,13]")
    @DeleteMapping
    protected ResponseData deletes(@NotEmpty(message="请选择要移除的记录") K[] ids){
        baseService.delete((Serializable[])ids);
        return ResponseData.success(null);
    }

    // 强制批量删除：比普通删除拥有更大的权限。
    // 例如某角色有关联的账户信息时普通的deletes为了保证关联安全无法删除，则在deletes可以删除。
    // 当然具体取决于每个实际业务需求，一般配合delete请求使用，例如询问用户是否继续删除。如果不重写则和普通删除一样。
    @PreAuthorize("@authCheckService.check(this, 'del')")
    @ApiOperation(value = "强制批量删除记录")
    @LimitRequest
    @ApiImplicitParam(name = "ids", value = "要删除的id集合", required = true,paramType = "query",example ="[11,12,13]")
    @DeleteMapping("/force")
    protected ResponseData forceDeletes(@NotEmpty(message="请选择要移除的记录") K[] ids){
        baseService.forceDelete((Serializable[])ids);
        return ResponseData.success(null);
    }
}
